📝 我的笔记

还没有笔记

选中页面文字后点击「高亮」按钮添加

信号

📜 原文
📖 逐步解释
∑ 公式拆解
💡 数值示例
⚠️ 易错点
📝 总结
🎯 存在目的
🧠 直觉心智模型
💭 直观想象

1信号

COMS W3157

Dr. Borowski

1 为什么需要信号?

考虑以下代码片段:

```

int main() {

while (1) {

sleep(5);

printf("Hello\n");

}

}

```

我们如何返回命令提示符?

2 为什么需要信号?

3 信号

信号是对一个事件的通知,该事件会引起操作系统的注意。信号可能来自:

4 信号与默认操作

| Number | Name | 默认操作 | 对应事件 |

| :--- | :--- | :--- | :--- |

| 1 | SIGHUP | 终止 | 终端线路挂断 |

| 2 | SIGINT | 终止 | 键盘中断 |

| 3 | SIGQUIT | 终止 | 键盘退出 |

| 4 | SIGILL | 终止 | 非法指令 |

| 5 | SIGTRAP | 终止并生成核心转储 ${ }^{\text {a }}$ | 跟踪陷阱 |

| 6 | SIGABRT | 终止并生成核心转储 ${ }^{\text {a }}$ | 来自 abort 函数的终止信号 |

| 7 | SIGBUS | 终止 | 总线错误 |

| 8 | SIGFPE | 终止并生成核心转储 ${ }^{\text {a }}$ | 浮点异常 |

| 9 | SIGKILL | 终止 ${ }^{\text {b }}$ | 杀死程序 |

| 10 | SIGUSR1 | 终止 | 用户定义信号 1 |

| 11 | SIGSEGV | 终止并生成核心转储 ${ }^{\text {a }}$ | 无效内存引用(段错误) |

| 12 | SIGUSR2 | 终止 | 用户定义信号 2 |

| 13 | SIGPIPE | 终止 | 写入无读取器的管道 |

| 14 | SIGALRM | 终止 | 来自 alarm 函数的定时器信号 |

| 15 | SIGTERM | 终止 | 软件终止信号 |

| 16 | SIGSTKFLT | 终止 | 协处理器栈错误 |

| 17 | SIGCHLD | 忽略 | 子进程已停止或终止 |

| 18 | SIGCONT | 忽略 | 如果停止则继续进程 |

| 19 | SIGSTOP | 停止直到下一个 SIGCONT ${ }^{\text {b }}$ | 非终端停止信号 |

| 20 | SIGTSTP | 停止直到下一个 SIGCONT | 来自终端的停止信号 |

| 21 | SIGTTIN | 停止直到下一个 SIGCONT | 后台进程从终端读取 |

| 22 | SIGTTOU | 停止直到下一个 SIGCONT | 后台进程写入终端 |

| 23 | SIGURG | 忽略 | 套接字上的紧急条件 |

| 24 | SIGXCPU | 终止 | CPU 时间限制超出 |

| 25 | SIGXFSZ | 终止 | 文件大小限制超出 |

| 26 | SIGVTALRM | 终止 | 虚拟定时器过期 |

| 27 | SIGPROF | 终止 | 分析定时器过期 |

| 28 | SIGWINCH | 忽略 | 窗口大小改变 |

| 29 | SIGIO | 终止 | 描述符上现在可以进行 I/O |

| 30 | SIGPWR | 终止 | 电源故障 |

Linux 信号。注:(a) 多年前,主内存采用一种称为磁芯内存(core memory)的技术实现。“生成核心转储”(Dumping core)是一个历史术语,指将代码和数据内存段的映像写入磁盘。(b) 该信号既不能被捕获也不能被忽略。(来源:man 7 signal。数据来自 Linux 基金会。)

5 信号生成函数

unsigned int alarm(unsigned int seconds) 设置一个警报,并在其过期时发送一个名为 SIGALRM 的信号。

int kill(pid_t pid, int sig) 将指定的信号发送到指定的进程。请注意,每个信号都与一个正整数相关联,这就是为什么 sig 作为 int 传递的原因。

6 信号生成函数

raise (int sig) 是 kill() 的一个特例,它将指定的信号发送到当前运行的进程。

7 接收到信号时

信号处理。接收到信号会触发控制转移到信号处理程序。处理完成后,处理程序将控制权返回给被中断的程序。

8 “旧式”信号处理

typedef void (*sighandler_t) (int);

sighandler_t signal

(int signum, sighandler_t handler)

设置当遇到信号 signum 时要使用的处理程序为 handler,并返回相同的处理程序。请注意,handler 是一个函数指针。

9 signal() 示例

```

#include

#include

#include

void sighandler(int signo) {

if (signo == SIGINT) {

printf("Received SIGINT\n");

}

}

int main() {

if (signal(SIGINT, sighandler) == SIG_ERR) {

printf("Can't catch SIGINT\n");

}

// A long sleep so we can issue a signal.

while (1) {

sleep (1);

}

return 0;

}

```

10 signal() 的问题

11 现代信号处理方式

int sigaction(int signum, const struct sigaction act, struct sigaction oldact)

12 sa_handler 与 sa_sigaction

```

struct sigaction {

void (*sa_handler) (int);

void (sa_sigaction)(int, siginfo_t , void *);

sigset_t sa_mask;

int sa_flags;

void (*sa_restorer) (void);

}

```

sa_handler 是接收到信号时调用的函数,而 sa_sigaction 是一个 sigaction 调用。它们是互斥的,不应同时设置。

13 sa_mask

```

struct sigaction {

void (*sa_handler) (int);

void (sa_sigaction)(int, siginfo_t , void *);

sigset_t sa_mask;

int sa_flags;

void (*sa_restorer) (void);

}

```

14 sa_flags

```

struct sigaction {

void (*sa_handler) (int);

void (sa_sigaction)(int, siginfo_t , void *);

sigset_t sa_mask;

int sa_flags;

void (*sa_restorer) (void);

}

```

15 sa_sigaction

16 sa_sigaction

siginfo_t 数据类型是一个包含以下字段的结构体:

```

siginfo_t {

int- si_signo; / Signal number /

int si-errno; / An errno value /

int si_code; / Signal code /

int si_trapno; /* Trap number that caused

hardware-generated signal

/ (unused on most architectures) */

pid_t si_pid; / Sending process ID /

uid_t si_uid; / Real user ID of sending process /

int- si_status; / Exit value or signal /

clock t si-utime; / User time consumed /

clock_t si-stime; / System time consumed /

union-sigvaI si_value; / Signal value /

int si_int; / POSIX.1b signal /

void si_ptr; / POSIX.1b signal */

int si_overrun; /* Timer overrun count;

POSIX.1b timers */

int si timerid; / Timer ID; POSIX.1b timers /

void si-addr; / Memory location which caused fault */

long si_band; /* Band event (was int in

int si_fd; / File descriptor /

short si_addr_lsb; /* Least significant bit of address

void si_lower; / Lower bound when address violation

occurred (since Linux 3.19) */

void si_upper; / Upper bound when address violation

occurred (since Linux 3.19) */

int si_pkey; /* Protection key on PTE that caused

fault (since Linux 4.6) */

void si_call_addr; / Address of system call instruction

(since Linux 3.5) */

int si_syscall; /* Number of attempted system call

(since Linux 3.5) */

unsigned int si_arch; /* Architecture of attempted system call

(since Linux 3.5) */

}

```

17 sa_sigaction 示例

```

#include

#include

#include

void handle(int sig, siginfo_t siginfo, void context) {

printf("Sending PID: %ld, UID: %ld\n",

(long)siginfo->si_pid, (long) siginfo->si_uid);

}

int main() {

struct sigaction act = {0};

// Use the sa_sigaction field because the handle has two additional

// parameters.

act.sa_sigaction = handle;

// The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not

// sa_handler.

act.sa_flags = SA_SIGINFO;

if (sigaction(SIGTERM, &act, NULL) < 0) {

perror("sigaction");

return 1;

}

while (1) { sleep(1); }

return 0;

}

```

18 系统调用中断

19 可重入函数

20 可重入函数

许多系统调用,如 read()、write() 和 getppid() 都是可重入的。这意味着如果信号中断了调用,它可以在信号处理程序执行完成后安全地重新启动。

21 可重入函数列表

| abort | faccessat | linkat | select | socketpair |

| :--- | :--- | :--- | :--- | :--- |

| accept | fchmod | listen | sem_post | stat |

| access | fchmodat | lseek | send | symlink |

| aio_error | fchown | lstat | sendmsg | symlinkat |

| aio_return | fchownat | mkdir | sendto | tcdrain |

| aio_suspend | fcntl | mkdirat | setgid | tcflow |

| alarm | fdatasync | mkfifo | setpgid | tcflush |

| bind | fexecve | mkfifoat | setsid | tcgetattr |

| cfgetispeed | fork | mknod | setsockopt | tcgetpgrp |

| cfgetospeed | fstat | mknodat | setuid | tcsendbreak |

| cfsetispeed | fstatat | open | shutdown | tcsetattr |

| cfsetospeed | fsync | openat | sigaction | tcsetpgrp |

| chdir | ftruncate | pause | sigaddset | time |

| chmod | futimens | pipe | sigdelset | timer_getoverrun |

| chown | getegid | poll | sigemptyset | timer_gettime |

| clock_gettime | geteuid | posix_trace_event | sigfillset | timer_settime |

| close | getgid | pselect | sigismember | times |

| connect | getgroups | raise | signal | umask |

| creat | getpeername | read | sigpause | uname |

| dup | getpgrp | readlink | sigpending | unlink |

| dup2 | getpid | readlinkat | sigprocmask | unlinkat |

| execl | getppid | recv | sigqueue | utime |

| execle | getsockname | recvfrom | sigset | utimensat |

| execv | getsockopt | recvmsg | sigsuspend | utimes |

| execve | getuid | rename | sleep | wait |

| _Exit | kill | renameat | sockatmark | waitpid |

| _exit | link | rmdir | socket | write |

图 10.4 可从信号处理程序调用的可重入函数